package com.soo.hoo.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.soo.hoo.admin.mapper.SysFileMapper;
import com.soo.hoo.admin.model.dto.FileDTO;
import com.soo.hoo.admin.model.dto.ImageFileDTO;
import com.soo.hoo.admin.model.entity.SysFile;
import com.soo.hoo.admin.service.FileService;
import com.soo.hoo.core.exception.GlobalException;
import com.soo.hoo.core.service.impl.ServiceImpl;
import jakarta.annotation.Nullable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>文件服务</p>
 *
 * @author 杨燚
 * @since 2023/6/18
 */
@Service
public class FileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> implements FileService {

    @Value("${file.upload.path}")
    private String fileUploadPath;

    /**
     * 分隔符
     */
    private static final String SEPARATOR = File.separator;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileDTO upload(MultipartFile file, @Nullable String module) {
        if (file == null || file.isEmpty()) {
            throw new GlobalException("请选择需要上传的文件");
        }
        return saveFile(file, module);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ImageFileDTO uploadImage(MultipartFile imageFile, @Nullable String module) {
        FileDTO upload = this.upload(imageFile, module);
        ImageFileDTO image = new ImageFileDTO(upload);
        image.setImagePath("/image/" + upload.getModule() + "/" + upload.getFilename());
        return image;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<FileDTO> upload(List<MultipartFile> files, String module) {
        if (CollUtil.isEmpty(files)) {
            throw new GlobalException("请选择需要上传的文件");
        }
        List<FileDTO> rets = new ArrayList<>(files.size());
        for (MultipartFile file : files) {
            FileDTO res = saveFile(file, module);
            rets.add(res);
        }

        return rets;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<ImageFileDTO> uploadImage(List<MultipartFile> files, String module) {
        List<FileDTO> upload = this.upload(files, module);
        return upload.stream().map(e -> {
            ImageFileDTO res = new ImageFileDTO(e);
            res.setImagePath("/image/" + e.getModule() + "/" + e.getFilename());
            return res;
        }).toList();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeFile(Collection<Long> ids) {
        if (CollUtil.isNotEmpty(ids)) {
            List<SysFile> fileInfoList = baseMapper.selectBatchIds(ids);
            String filePath;
            if (CollUtil.isNotEmpty(fileInfoList)) {
                for (SysFile fileInfo : fileInfoList) {
                    filePath = fileUploadPath + SEPARATOR + fileInfo.getModule() + SEPARATOR + fileInfo.getFilename();
                    try {
                        FileUtil.del(filePath);
                    } catch (Exception e) {
                        throw new GlobalException("删除文件【" + fileInfo.getOriginalFilename() + "】失败：" + e.getMessage());
                    }
                    baseMapper.deleteById(fileInfo);
                }
            }
        }
    }

    @Override
    public List<FileDTO> getFileInfo(List<Long> ids) {
        List<SysFile> files = baseMapper.selectBatchIds(ids);
        return files.stream().map(
                e -> FileDTO.builder()
                        .id(e.getId())
                        .filename(e.getFilename())
                        .originalFilename(e.getOriginalFilename())
                        .module(e.getModule())
                        .path(fileUploadPath + SEPARATOR + e.getModule() + SEPARATOR + e.getFilename())
                        .size(e.getSize())
                        .build()
        ).toList();
    }

    /**
     * 保存文件信息，并上传文件到指定目录
     *
     * @param file   文件
     * @param module 模块
     * @return 保存后信息
     */
    private FileDTO saveFile(MultipartFile file, @Nullable String module) {
        if (StrUtil.isBlank(module)) {
            module = "common";
        }
        String originalFilename = file.getOriginalFilename() == null ? file.getName() : file.getOriginalFilename();
        String extName = FileUtil.extName(originalFilename);
        String filename = IdUtil.fastSimpleUUID() + "." + extName;
        long size = file.getSize();
        SysFile sysFile = new SysFile();
        sysFile.setFilename(filename);
        sysFile.setOriginalFilename(originalFilename);
        sysFile.setFileSuffix(extName);
        sysFile.setSize(size);
        sysFile.setModule(module);
        baseMapper.insert(sysFile);
        // 上传文件到目标路径
        String filePath = fileUploadPath + SEPARATOR + module + SEPARATOR + filename;
        try {
            FileUtil.writeFromStream(file.getInputStream(), filePath);
        } catch (IOException e) {
            throw new GlobalException("文件上传异常：" + e.getMessage());
        }
        FileDTO res = new FileDTO();
        res.setId(sysFile.getId());
        res.setModule(module);
        res.setFilename(sysFile.getFilename());
        res.setOriginalFilename(originalFilename);
        res.setSize(size);
        res.setPath(filePath);
        return res;
    }


    @Override
    public List<ImageFileDTO> getImageFileInfo(List<Long> ids) {
        List<SysFile> files = baseMapper.selectBatchIds(ids);
        return files.stream().map(
                e -> {
                    ImageFileDTO res = new ImageFileDTO();
                    BeanUtil.copyProperties(e, res);
                    res.setPath(fileUploadPath + SEPARATOR + e.getModule() + SEPARATOR + e.getFilename());
                    res.setImagePath("/image/" + e.getModule() + "/" + e.getFilename());
                    return res;
                }
        ).toList();
    }


    @Override
    public Map<Long, ImageFileDTO> getImageFileInfoMap(List<Long> ids) {
        return this.getImageFileInfo(ids).stream().collect(Collectors.toMap(FileDTO::getId, e -> e));
    }
}
